package com.wangjiang.mario.admin.core.shiro.realm;

import com.wangjiang.mario.admin.core.shiro.token.JwtToken;
import com.wangjiang.mario.admin.common.constant.JwtConstant;
import com.wangjiang.mario.admin.common.util.JwtUtil;
import com.wangjiang.mario.admin.system.domain.components.CacheComponent;
import com.wangjiang.mario.admin.system.domain.service.JwtService;
import com.wangjiang.mario.admin.system.domain.service.UserService;
import com.wangjiang.mario.admin.system.object.user.UserRoleMenuVo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.MalformedJwtException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.Map;

/* *
 * @Author tomsun28
 * @Description
 * @Date 18:07 2018/3/3
 */
public class JwtRealm extends AuthorizingRealm {

    @Autowired
    CacheComponent cacheComponent;

    @Autowired
    UserService userService;

    @Autowired
    JwtService jwtService;

    private static final transient Logger log = LoggerFactory.getLogger(JwtRealm.class);


    public Class<?> getAuthenticationTokenClass() {
        // 此realm只支持jwtToken
        return JwtToken.class;
    }


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("=========执行授权, 获取权限， 需要权限验证时调用===========");
        Map principal = (HashMap) principalCollection.getPrimaryPrincipal();
        Jws<Claims> payload = (Jws<Claims>) principal.get("payload");
        Claims body = payload.getBody();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 在用户服务中获取所有的权限
        String uuid = body.get(JwtConstant.JWT_BODY_UUID).toString();
        UserRoleMenuVo user = (UserRoleMenuVo) principal.get("user");
        // 超管有所有权限
        if(user.isAdmin()) {
            info.addRole("admin");
            info.addStringPermission("*:*:*");
        } else {
            // 这里还是重新查一次 防止新的数据跟新
            user = userService.getUserWithRoleAndMenuByUUid(uuid);
            info.setRoles(user.getRoles());
            info.addStringPermissions(user.getPerms());
        }

        return info;
    }

    /**
     * 登录
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("=========jwt头部验证,subject.login(token) c 时调用===========");

        if (!(authenticationToken instanceof JwtToken)) {
            return null;
        }

        JwtToken jwtToken = (JwtToken) authenticationToken;
        String jwt = (String) jwtToken.getCredentials();
        UserRoleMenuVo user;
        Jws<Claims> payload;
        try {
            if (JwtUtil.checkJWT(jwt)) {
                payload = JwtUtil.parseJWT(jwt);
                // 校验redis里边的token是否与该token一致，不一致则token无效
                Claims body = payload.getBody();
                String userUUid = body.get(JwtConstant.JWT_BODY_UUID).toString();
                user = userService.getUserWithRoleAndMenuByUUid(userUUid);
                // 如果redis中没有该记录，或该记录不等于传入的jwt字符串。则token无效
                if (!(jwtService.existsTokenInCache(userUUid, jwt))) {
                    throw new Exception("jwt 不存在或过期");
                }
            } else {
                throw new Exception("jwt token 格式不正确");
            }
            // 预先解析Payload
            // 没有做任何的签名校验
        } catch (MalformedJwtException e) {
            throw new AuthenticationException("errJwt");     //令牌格式错误
        } catch (Exception e) {
            throw new AuthenticationException("errsJwt");    //令牌无效
        }
        if (null == payload) {
            throw new AuthenticationException("errJwt");    //令牌无效
        }

        Map<String, Object> principle = new HashMap<>();
        principle.put("payload", payload);
        principle.put("user", user);

        return new SimpleAuthenticationInfo(principle, jwt, this.getName());
    }
}
